#pragma once

//////////////////////////////////////////////////////////////////////////
//
// Init and destroy shaders and input layout used in the application
//
//////////////////////////////////////////////////////////////////////////

#include <cstdint>
#include <d3d11.h>
#include <unordered_map>
#include <string>

struct ID3D11ComputeShader;
struct ID3D11DomainShader;
struct ID3D11GeometryShader;
struct ID3D11HullShader;
struct ID3D11InputLayout;
struct ID3D11PixelShader;
struct ID3D11VertexShader;

class ShadersManager {
	typedef std::unordered_map<std::wstring, ID3D11InputLayout*> InputLayouts;
	typedef std::unordered_map<std::wstring, ID3D11VertexShader*> VertexShaders;
	typedef std::unordered_map<std::wstring, ID3D11PixelShader*> PixelShaders;
	typedef std::unordered_map<std::wstring, ID3D11HullShader*> HullShaders;
	typedef std::unordered_map<std::wstring, ID3D11DomainShader*> DomainShaders;
	typedef std::unordered_map<std::wstring, ID3D11ComputeShader*> ComputeShaders;
	typedef std::unordered_map<std::wstring, ID3D11GeometryShader*> GeometryShaders;

	InputLayouts mInputLayouts;
	VertexShaders mVertexShaders;
	PixelShaders mPixelShaders;
	HullShaders mHullShaders;
	DomainShaders mDomainShaders;
	ComputeShaders mComputeShaders;
	GeometryShaders mGeometryShaders;

public:
	// Return nullptr if shader cannot be added successfully
	// Shader should be previously compiled to .cso
	// Example: addVertexShader("myVertexShader", "hlsl/vs.cso", inputElemsDesc)
	// In addVertexShader(), inputElemsDesc is for creating inputlayout, or null otherwise
	ID3D11VertexShader* addVertexShader(const std::wstring& shaderName, const std::wstring& filepath, const std::wstring* inputLayoutName, const D3D11_INPUT_ELEMENT_DESC* inputLayoutDesc, const uint32_t* descNumElems);
	ID3D11PixelShader* addPixelShader(const std::wstring& name, const std::wstring& filepath);
	ID3D11HullShader* addHullShader(const std::wstring& name, const std::wstring& filepath);
	ID3D11DomainShader* addDomainShader(const std::wstring& name, const std::wstring& filepath);
	ID3D11GeometryShader* addGeometryShader(const std::wstring& name, const std::wstring& filepath);
	ID3D11ComputeShader* addComputeShader(const std::wstring& name, const std::wstring& filepath);

	ID3D11InputLayout* inputLayout(const std::wstring& name) const;
	ID3D11VertexShader* vertexShader(const std::wstring& name) const;
	ID3D11PixelShader* pixelShader(const std::wstring& name) const;
	ID3D11HullShader* hullShader(const std::wstring& name) const;
	ID3D11DomainShader* domainShader(const std::wstring& name) const;
	ID3D11GeometryShader* geometryShader(const std::wstring& name) const;
	ID3D11ComputeShader* computeShader(const std::wstring& name) const;
    
	~ShadersManager();

private:
	void storeShaderByteCode(const std::wstring& fileName, std::vector<char>& buffer) const;
	void buildVertexLayout(std::vector<char>& shaderByteCode, const D3D11_INPUT_ELEMENT_DESC* inputLayoutDesc, const uint32_t inputLayoutDescSize, ID3D11InputLayout* &inputLayout) const;
};


